Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update explainer for the new spec #137

Merged
merged 18 commits into from
Feb 25, 2021
Merged

Conversation

aheejin
Copy link
Member

@aheejin aheejin commented Oct 7, 2020

This updates the explainer text according to the new spec we agreed in
the 09-15-2020 CG meeting. Some of the text is taken from the first
version

of the proposal. I renamed catch_br to delegate based on the
perceived consensus in #133.

There are some minor issues that need clarification, some of which is
currently being discussed in the repo:

  • rethrow's immmediate (or label) argument. I made it match the status
    in the first proposal for now. I believe keeping it makes it more
    versatile and lets us handle more languages.
  • It is not clear whether rethrow and delegate's label (=immediate)
    argument should count non-try block-like structures, such as blocks
    and loops. It is currently being discussed for delegate.
  • delegate's opcode should be decided. We exhausted all control-flow
    opcodes that begin with 0x0.

This updates the explainer text according to the new spec we agreed in
the 09-15-2020 CG meeting. Some of the text is taken from [the first
version](https://github.com/WebAssembly/exception-handling/blob/master/proposals/old/Exceptions-v1.md)
of the proposal. I renamed `catch_br` to `delegate` based on the
perceived consensus in WebAssembly#133.

There are some minor issues that need clarification, some of which is
currently being discussed in the repo:
- `rethrow`'s immmediate (or label) argument. I made it match the status
  in the first proposal for now. I believe keeping it makes it more
  versatile and lets us handle more languages.
- It is not clear whether `rethrow` and `delegate`'s label (=immediate)
  argument should count non-try block-like structures, such as blocks
  and loops. It is currently being discussed for `delegate`.
- `delegate`'s opcode should be decided. We exhausted all control-flow
  opcodes that begin with `0x0`.

The `unwind` block is meant to contain cleanup instructions, such as
destructors, in case any instruction in the corresponding try block throws.
Currently the `unwind` instruction has the same semantics as the `catch_all`
Copy link
Collaborator

@ioannad ioannad Oct 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised and confused with these semantics of unwind.

If try ... unwind ... end is semantically equivalent to try ... catch_all ... rethrow end, then I can't see why we are adding this instruction. Can't we just postpone this try ... unwind ... end to a future two-phase unwinding proposal? In fact, if the unwind semantics will change later, couldn't that create backwards compatibility problems?

Until this document, I had understood unwind more as a primitive related to common-lisp's unwind-protect, which btw seems to be related to Java's and Python's finally instead of catch_all.

Copy link
Collaborator

@ioannad ioannad Oct 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also it seems that unwind could spark a very long discussion, for example when common-lisp was being specified, unwind-protect related discussions spanned over years (see this link, at the bottom of the page).

If we can avoid adding this instruction now, we could have these discussions in a different proposal.

Copy link
Contributor

@RossTate RossTate Oct 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ioannad It's good to point out that there are good reasons to be wary of adding control constructs. Fortunately, though, in the case of unwind, the control construct has been tried out in a wide variety of languages (with finally being a close relative) and its semantics are fairly well understood. It seems to have been found to be particularly useful in multi-language settings for reasons akin to why it was added here to make modules forwards compatible with two-phase exception handling. And nowadays its semantics are fairly well understood, at least at the level that WebAssembly operates at. (This wasn't the case back when Common Lisp was being specified, and some of the complications in the referenced discussion have to do with higher-level considerations.)

I cannot say the same for catch_all or rethrow though, so rather than removing unwind due to the above equivalence, my suggestion would be to remove catch_all instead (if you were to remove anything at all).

Copy link
Member Author

@aheejin aheejin Oct 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ioannad They have the same semantics, and they are planned to have different semantics in two-phase. In single-phase unwinding, both catch_all's and unwind's bodies run as we search up and unwind the stack. In two-phase, catch bodies run after we finish all two-phase unwinding process, while unwind bodies run as cleanups in the second phase, and their end instruction act as "resuming the second phase" instruction.

I think what you said also makes sense that if two are equivalent, why adding them now? I don't have a strong opinion either way myself. It was taken from @RossTate's suggestion, and I agreed with that for the reason that it'd be good if we make the current proposal and the follow-on future two-phase proposal as similar as possible, so that toolchains targeting EH proposals don't need to change where to put their cleanup code when they switch to the next proposal. (But there will be some other changes necessary for toolchains.)

@RossTate As I said in the other issue, catch_all has its uses. If we remove that, unwind cannot replace it when two-phase arrives. These days, I am almost getting an impression that you are trying to remove every single instruction that you don't need yourself for your future proposals. (By the way, I don't want this PR to be another discussion for removal of instructions.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

though, in the case of unwind, the control construct has been tried out in a wide variety of languages

@RossTate, can you point us to any such language? Constructs like finally or unwind-protect seem much more common-place and semantically well-understood.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I understand, you would have to make significant changes to LLVM to get that to happen, including changing how it represents CFGs and exception handling. My take is that finally is a surface-level construct, and that unwind is part of its lower-level counterpart, with code duplication or #124 providing ways to support the rest of its lower-level counterpart.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the LLVM backend gets by without any use of finally/unwind right now, so, shrug? And of course it is trivial to express fault a.k.a. unwind with such a flagged finally if needs be.

I'd still be interested in knowing which .NET producers actually use fault.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the LLVM backend gets by without any use of finally/unwind right now, so, shrug?

Every function call in LLVM has an argument specifying how to unwind it.

I'd still be interested in knowing which .NET producers actually use fault.

It's how .NET addresses the problem above that C++ destructors have different semantics on the two kinds of exit paths.

Copy link
Member Author

@aheejin aheejin Oct 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rossberg

About LLVM: It compiles destructors for the normal path and the exception path separately, because the exception path contains some different logic, such as, if another exception arises within there, it does not throw but calls std::terminate or something. This is C++, but it also compiles finally away for some languages that have finally, such as Objective-C.

In the CFG level, LLVM has an invoke instruction, which is a variant of call instruction. invoke takes two successors: a normal destination, where we should go when the call returns normally, and unwind destination, where we should unwind to when the call throws. CFG's control flow logic is very direct and simple; all are just go-to edges (or unwind edges) and they are directly encoded. Our wasm backend lowers this to try-catch.

So my point is, if we introduce finally as a replacement of unwind, at least for LLVM it is not usable for us. And I am not 100% sure if I understood your fragmented stack argument, but I think finally has all the same problem anyway, no?

We can still use catch for cleanup code as we have so far, but for 2PEH we need to distinguish user catch handler from cleanup code. That doesn't have to be unwind, but what I'm saying is we need a way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I can see that it's not useful with the specific choices LLVM made. But other compilers could benefit. If there is pressure to add finally, then we shouldn't end up having both unwind and finally, thus the idea of a simple generalisation that expresses both.

And I am not 100% sure if I understood your fragmented stack argument, but I think finally has all the same problem anyway, no?

That is unrelated. That problem problem occurs with the search phase, not the unwind phase. The latter runs on the top of the stack, since all other stack frames are already unwound at that point.

proposals/Exceptions.md Outdated Show resolved Hide resolved

The current plan is, try-unwind blocks will have a different semantics from
that of try-catch blocks when we introduce two-phase unwinding as a follow-on
proposal in the future.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would really help if this plan could be sketched in this explainer somehow (or some separate doc to point to). Because to be honest, I still don't understand it, nor how the current semantics of unwind could be changed/generalised later in a backwards-compatible manner.


The `unwind` block is meant to contain cleanup instructions, such as
destructors, in case any instruction in the corresponding try block throws.
Currently the `unwind` instruction has the same semantics as the `catch_all`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

though, in the case of unwind, the control construct has been tried out in a wide variety of languages

@RossTate, can you point us to any such language? Constructs like finally or unwind-protect seem much more common-place and semantically well-understood.

ioannad added a commit to ioannad/exception-handling that referenced this pull request Nov 10, 2020
This is an attempt to formally describe @aheejin's 3rd proposal, which she presented to the Wasm CG, and which was voted to be the new EH proposal, on September 18, 2020. This is not formal spec that I have developed, but a formal description of this 3rd proposal.

This is a reworked form of my [first attempt on this formal spec overview](WebAssembly#87 (comment)) edited with my new understanding of the spec based on the discussion below, and in other issues, and according to @aheejin 's [3rd proposal overview](https://github.com/WebAssembly/exception-handling/blob/f7a4f60d11fb6326fc13f84d3889b11d3873f08a/proposals/Exceptions.md) in PR WebAssembly#137.

This is in the form of a document as @tlively [requested](WebAssembly#142 (comment)), to make discussion on specific points easier.

I wrote this formal spec overview roughly in the style of the 2nd proposal's [formal spec overview](WebAssembly#87 (comment)) by @rossberg.

Particular points of interest:

- In this I assume `rethrow` does have an immediate, as it is now described in WebAssembly#137.
- The instruction `unwind` now reduces to `catch_all ... rethrow` as specified in Heejin's overview.
- Because unwind is much simpler in this MVP, there is no `throw_point` anymore and `caught_m` is much simpler.
- The introduction of `caught_m` now also introduces a label around the catch instructions, which label a rethrow instruction can reference.
- Added explanation of the peculiar side condition in try's execution rule - just trying to make the rules more compact.

I would be happy if anyone could point out things that are wrong or that I misunderstood.
@mf-RDP
Copy link

mf-RDP commented Dec 13, 2020

Aheejin,

I just want to ask a few questions:

  • When, do you guess, can we expect to see this in LLVM/clang as a (partially) working, first implementation, can we expect Q1/2021 or later?
  • For clang++: will the current required WASM<>ABI implementation (_Unwind_CallPersonality, __cxa_begin_catch etc.) stay the same? I really hope so, also at least for the catchpad data layout in data segments.

Many Thanks
Sascha

@aheejin
Copy link
Member Author

aheejin commented Dec 15, 2020

@mf-RDP

  • When, do you guess, can we expect to see this in LLVM/clang as a (partially) working, first implementation, can we expect Q1/2021 or later?

Can't guarantee an exact timeline, but we hope we can get it work with LLVM/Clang/Emscripten at least within Q1 2021.

  • For clang++: will the current required WASM<>ABI implementation (_Unwind_CallPersonality, __cxa_begin_catch etc.) stay the same? I really hope so, also at least for the catchpad data layout in data segments.

I think they will stay mostly the same. At least so far I don't plat to change them.

aheejin added a commit to llvm/llvm-project that referenced this pull request Feb 13, 2021
Previously we assumed `rethrow`'s argument was always 0, but it turned
out `rethrow` follows the same rule with `br` or `delegate`:
WebAssembly/exception-handling#137
WebAssembly/exception-handling#146 (comment)

Currently `rethrow`s generated by our backend always rethrow the
exception caught by the innermost enclosing catch, so this adds a
function to compute that and replaces `rethrow`'s argument with its
computed result.

This also renames `EHPadStack` in `InstPrinter` to `TryStack`, because
in CFGStackify we use `EHPadStack` to mean the range between
`catch`~`end`, while in `InstPrinter` we used it to mean the range
between `try`~`catch`, so choosing different names would look clearer.
Doesn't contain any functional changes in `InstPrinter`.

Reviewed By: dschuff

Differential Revision: https://reviews.llvm.org/D96595
@takikawa
Copy link
Collaborator

I read through the new explainer text and it looks good to me, I agree with merging it and sorting out remaining issues such as try-unwind typing after.

Copy link
Member

@tlively tlively left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have just a few comments, but otherwise LGTM!

A try-catch block contains zero or more `catch` blocks and zero or one
`catch_all` block. All `catch` blocks should precede the `catch_all` block, if
any. The `catch`/`catch_all` instructions (within the try construct) are called
the _catching_ instructions. There should be at least `catch` or `catch_all`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
the _catching_ instructions. There should be at least `catch` or `catch_all`
the _catching_ instructions. There should be at least one `catch` or `catch_all`

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd actually drop this requirement. There is no particular reason to disallow (i.e., special-case) the empty handler list.

Copy link
Member Author

@aheejin aheejin Feb 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tlively Done.
@rossberg What would that mean then? Wouldn't that be just the same as a block?

Copy link
Member

@rossberg rossberg Feb 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but it's always preferable to avoid discontinuities and corner case restrictions unless there's a good reason to have them. AFAIK, there is no other case where we impose such a restriction. For example, sections can be empty a block can be empty, although that's equivalent to a nop, etc. -- there are various cases like this that are seemingly useless but allowed. Not having to worry about discontinuities can ease life for consumers and even more so, for producers. For example, if a producer generates a construct from a list of something, it doesn't need to implement extra logic for the empty list.

Copy link
Member Author

@aheejin aheejin Feb 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think try-catch is a little different case then just having a list of of something (e.g. functions) that can be empty. It was designed as a two-parts instruction from the beginning, and while I don't think it's a groundbreaking change, many parts of toolchain have to treat try without catch as a special case, because most parts are written under the assumption there will be two parts.

Also I think this is a kind of big change that has not been discussed so far, so I think it warrants a separate discussion thread..?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are still two parts, but one is a list, and the list can have any length, including length 0. :) I don't think this is different from any other similar case, where we have deliberately refrained from making length 0 a special case -- there's not a single occurrence of X^+ in the entire Wasm grammar. :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, maybe that doesn't need to be treated as a special case. But anyway, I think we should make this a separate issue post first for more visibility than just changing this here and merging it in.

proposals/exception-handling/Exceptions.md Outdated Show resolved Hide resolved
proposals/exception-handling/Exceptions.md Outdated Show resolved Hide resolved
on top of the stack is popped and then thrown. The `rethrow` instruction traps
if the value on the top of the stack is null.
The `rethrow` instruction can only appear in the body of a catch/catch_all block
but not within the body of an unwind block. It always re-throws the exception
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think saying that rethrow cannot appear in an unwind block is too restrictive, consider the following example:

try
...
catch
  try
  ...
  unwind
    rethrow 1
  end
end

The rethrow appears in the unwind block, but IIUC this is ok because we never rethrow its exception. Should the rule rather be something like "the rethrow immediate can only be a catch or catch_all block"?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't understand why we are making this restriction. IIUC, for N > 0 the following code

try bt
  instr1*
unwind
  instr2*
  rethrow N
  instr3*
end

is equivalent to

try bt
  instr1*
unwind
  instr2*
  br 0
  instr3*
end
rethrow N-1

For N=0,

try bt
  instr1*
unwind
  instr2*
  rethrow 0
  instr3*
end

is equivalent to

try bt
  instr1*
unwind
  instr2*
end

In both cases, instr3* is redundant.

So I don't understand what situation is prevented with this restriction. Am I (still) misunderstanding the semantics of unwind?

(cc. @RossTate)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarification: I do agree with @thibaudmichaud that, if we do add a restriction, then it should be on the rethrow target (i.e., that rethrow cannot target an unwind block).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed with @thibaudmichaud and @ioannad. The restriction is stated the wrong way. It should simply say that the rethrow target must be a catch block, which automatically implies that it has to be properly nested into one.

I also agree that there is no particular reason to disallow rethrow targetting unwind.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with comments above. This restriction was basically requested by @RossTate. @RossTate, do you think it is OK to remove this restriction, as long as we don't allow rethrowing an exception caught by unwind?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forbidding rethrow would make sense for finally, because that has nothing to rethrow if the block was entered regularly, but no real point for unwind (unless one assumes we will be adding yet some other form of abort that is neither a trap nor an exception, but that's hardly desirable). But it's conservative to disallow it, so okay to leave it out for now.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rossberg IIUC also having br, br_if, br_table, or return among the instructions of an unwind clause would abort the unwinding process.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A rethrow that "targets" unwind is problematic because unwind can fire due to things besides exceptions. But a rethrow that is simply a throw of a caught exception should be allowed within unwind, just like throw is. (For supporting Common Lisp, it is necessary that exceptions be throwable from within unwind clauses.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ioannad, but branches do not invoke intermediate unwind clauses, IIUC.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I clarified the rethrow-unwind relationship to match the rule we agreed on here: 4d6c635

proposals/exception-handling/Exceptions.md Outdated Show resolved Hide resolved
A try-catch block contains zero or more `catch` blocks and zero or one
`catch_all` block. All `catch` blocks should precede the `catch_all` block, if
any. The `catch`/`catch_all` instructions (within the try construct) are called
the _catching_ instructions. There should be at least `catch` or `catch_all`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd actually drop this requirement. There is no particular reason to disallow (i.e., special-case) the empty handler list.

proposals/exception-handling/Exceptions.md Show resolved Hide resolved
proposals/exception-handling/Exceptions.md Show resolved Hide resolved
proposals/exception-handling/Exceptions.md Outdated Show resolved Hide resolved
proposals/exception-handling/Exceptions.md Outdated Show resolved Hide resolved
proposals/exception-handling/Exceptions.md Outdated Show resolved Hide resolved
`catch_all` instructions.
When the specified label within a `delegate`
instruction does not correspond to a `try` instruction, it is a validation
failure.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, this is an unnecessary restriction as well. We could specify it as simply being equivalent to a rethrow inside the target block, which makes it agnostic to what sort of construct it is. That also increases flexibility, because it allows to delegate to an enclosing handler that's not in lexical scope, which is more in line with the dynamically scoped nature of exception handlers.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed this at length in some of comments in #143 and #146, and it seems people there, including me, agreed that validation failure is clearer. I also would like to strongly advocate for this, because assuming exception can slip until it meets any enclosing try will make toolchain optimization, such as in Binaryen, very difficult.

proposals/exception-handling/Exceptions.md Outdated Show resolved Hide resolved
@@ -326,20 +381,20 @@ document](https://github.com/WebAssembly/spec/blob/master/document/core/text/ins
The following rules are added to *instructions*:

```
try blocktype instruction* catch instruction* end |
try blocktype instruction* (catch instruction*)+ (catch_all instruction*)+ end |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This grammar would require at least one catch and one catch_all, and allows multiple of the latter. I think you want

Suggested change
try blocktype instruction* (catch instruction*)+ (catch_all instruction*)+ end |
try blocktype instruction* (catch instruction*)* (catch_all instruction*)? end |

This allows a try with no catch blocks, which I think is preferable anyway. If you want to rule that out, you'll need to complicate the grammar.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the catch. But I'm not sure why we would need catchless try, given that it is the same as a block? This is something we haven't discussed so far, so do you mind if we discuss this in somewhere else and add to the explainer if we decide to do that? For now I'll fix this to match the current spec.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just want to add that issue #131 by @tlively also asks whether a try without further clauses could be allowed or not.

ioannad added a commit to ioannad/exception-handling that referenced this pull request Feb 19, 2021
As of commit 275c449

- `rethrow` is as in the first proposal.
- Labels do get a new attribute `kind` which is set to `try` or
  `catch' for labels surrounding instructions which start with
  `try` or `catch` respectively, and empty otherwise. This is used
  to validate `delegate` and `rethrow`/`unwind` respectively.
- `unwind` can no longer be a target of `rethrow`'s immediate
- The `Caught` stack is removed.

I also added a file with Wasm code examples from comments (referenced),
and what they reduce to according to these semantics.

The first example is the only one with a full reduction, and it uses all
new instructions, so it's hopefully easy to get an idea of how this works,
even for readers without much formal spec involvement.
@aheejin
Copy link
Member Author

aheejin commented Feb 24, 2021

Thanks for all the comments. I think I addressed most of them. One suggestion was whether we should allow catchless trys, but I think that discussion can continue in a separate issue and can be merged to the explainer later if we decide to make changes.

I'll merge this shortly if no one has any more comments.

@aheejin aheejin merged commit 176d5c3 into WebAssembly:master Feb 25, 2021
@aheejin aheejin deleted the explainer branch February 25, 2021 18:37
ioannad added a commit to ioannad/exception-handling that referenced this pull request Mar 12, 2021
This is an attempt to formally describe @aheejin's 3rd proposal, which she presented to the Wasm CG, and which was voted to be the new EH proposal, on September 18, 2020. This is not formal spec that I have developed, but a formal description of this 3rd proposal.

This is a reworked form of my [first attempt on this formal spec overview](WebAssembly#87 (comment)) edited with my new understanding of the spec based on the discussion below, and in other issues, and according to @aheejin 's [3rd proposal overview](https://github.com/WebAssembly/exception-handling/blob/f7a4f60d11fb6326fc13f84d3889b11d3873f08a/proposals/Exceptions.md) in PR WebAssembly#137.

This is in the form of a document as @tlively [requested](WebAssembly#142 (comment)), to make discussion on specific points easier.

I wrote this formal spec overview roughly in the style of the 2nd proposal's [formal spec overview](WebAssembly#87 (comment)) by @rossberg.

Particular points of interest:

- In this I assume `rethrow` does have an immediate, as it is now described in WebAssembly#137.
- The instruction `unwind` now reduces to `catch_all ... rethrow` as specified in Heejin's overview.
- Because unwind is much simpler in this MVP, there is no `throw_point` anymore and `caught_m` is much simpler.
- The introduction of `caught_m` now also introduces a label around the catch instructions, which label a rethrow instruction can reference.
- Added explanation of the peculiar side condition in try's execution rule - just trying to make the rules more compact.

I would be happy if anyone could point out things that are wrong or that I misunderstood.
ioannad added a commit to ioannad/exception-handling that referenced this pull request Mar 12, 2021
As of commit 275c449

- `rethrow` is as in the first proposal.
- Labels do get a new attribute `kind` which is set to `try` or
  `catch' for labels surrounding instructions which start with
  `try` or `catch` respectively, and empty otherwise. This is used
  to validate `delegate` and `rethrow`/`unwind` respectively.
- `unwind` can no longer be a target of `rethrow`'s immediate
- The `Caught` stack is removed.

I also added a file with Wasm code examples from comments (referenced),
and what they reduce to according to these semantics.

The first example is the only one with a full reduction, and it uses all
new instructions, so it's hopefully easy to get an idea of how this works,
even for readers without much formal spec involvement.
Ms2ger pushed a commit to Ms2ger/exception-handling that referenced this pull request Jun 24, 2021
ioannad added a commit to ioannad/exception-handling that referenced this pull request Jun 25, 2021
This is an attempt to formally describe @aheejin's 3rd proposal, which she presented to the Wasm CG, and which was voted to be the new EH proposal, on September 18, 2020. This is not formal spec that I have developed, but a formal description of this 3rd proposal.

This is a reworked form of my [first attempt on this formal spec overview](WebAssembly#87 (comment)) edited with my new understanding of the spec based on the discussion below, and in other issues, and according to @aheejin 's [3rd proposal overview](https://github.com/WebAssembly/exception-handling/blob/f7a4f60d11fb6326fc13f84d3889b11d3873f08a/proposals/Exceptions.md) in PR WebAssembly#137.

This is in the form of a document as @tlively [requested](WebAssembly#142 (comment)), to make discussion on specific points easier.

I wrote this formal spec overview roughly in the style of the 2nd proposal's [formal spec overview](WebAssembly#87 (comment)) by @rossberg.

Particular points of interest:

- In this I assume `rethrow` does have an immediate, as it is now described in WebAssembly#137.
- The instruction `unwind` now reduces to `catch_all ... rethrow` as specified in Heejin's overview.
- Because unwind is much simpler in this MVP, there is no `throw_point` anymore and `caught_m` is much simpler.
- The introduction of `caught_m` now also introduces a label around the catch instructions, which label a rethrow instruction can reference.
- Added explanation of the peculiar side condition in try's execution rule - just trying to make the rules more compact.

I would be happy if anyone could point out things that are wrong or that I misunderstood.
ioannad added a commit to ioannad/exception-handling that referenced this pull request Jun 25, 2021
As of commit 275c449

- `rethrow` is as in the first proposal.
- Labels do get a new attribute `kind` which is set to `try` or
  `catch' for labels surrounding instructions which start with
  `try` or `catch` respectively, and empty otherwise. This is used
  to validate `delegate` and `rethrow`/`unwind` respectively.
- `unwind` can no longer be a target of `rethrow`'s immediate
- The `Caught` stack is removed.

I also added a file with Wasm code examples from comments (referenced),
and what they reduce to according to these semantics.

The first example is the only one with a full reduction, and it uses all
new instructions, so it's hopefully easy to get an idea of how this works,
even for readers without much formal spec involvement.
mem-frob pushed a commit to draperlaboratory/hope-llvm-project that referenced this pull request Oct 7, 2022
Previously we assumed `rethrow`'s argument was always 0, but it turned
out `rethrow` follows the same rule with `br` or `delegate`:
WebAssembly/exception-handling#137
WebAssembly/exception-handling#146 (comment)

Currently `rethrow`s generated by our backend always rethrow the
exception caught by the innermost enclosing catch, so this adds a
function to compute that and replaces `rethrow`'s argument with its
computed result.

This also renames `EHPadStack` in `InstPrinter` to `TryStack`, because
in CFGStackify we use `EHPadStack` to mean the range between
`catch`~`end`, while in `InstPrinter` we used it to mean the range
between `try`~`catch`, so choosing different names would look clearer.
Doesn't contain any functional changes in `InstPrinter`.

Reviewed By: dschuff

Differential Revision: https://reviews.llvm.org/D96595
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants